package com.zhima.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhima.dto.QueryQuestionCondition;
import com.zhima.dto.QuestionDoc;
import com.zhima.entity.OjQuestionPublish;
import com.zhima.enums.QuestionScope;
import com.zhima.exception.ErrorCode;
import com.zhima.exception.ZhimaOjException;
import com.zhima.service.ElasticSearchService;
import com.zhima.vo.QuestionTableVO;
import com.zhima.vo.SimpleLabelVO;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.zhima.constant.QuestionElasticConstant.QUESTION_INDEX;

@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public List<QuestionTableVO> searchQuestions(QueryQuestionCondition queryQuestionCondition, Long pageNum, Long pageSize, Page<QuestionTableVO> ans) {
        // 使用ES做模糊搜索
        // 标签的匹配技术实现
        //      想法一：先根据其他条件查出数据，然后匹配是否符合题目标签的要求，最后条数不满的话就递归查询（麻烦，并且对于页码的控制比较反人类，有点滚动分页那味）
        //      √想法二：直接利用es里面有关于数组的match条件查询
        // 搜索条件有这些
        // 题目难度√
        // 题目id√
        // 题目名（模糊）
        // 题目内容（模糊）
        // 题目标签（包含）
        SearchRequest searchRequest = new SearchRequest(QUESTION_INDEX);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        buildBoolQueryBuilder(boolQueryBuilder, queryQuestionCondition);
        // 分页参数
        searchRequest.source().from((int) ((pageNum - 1) * pageSize)).size(pageSize.intValue());
        // 设置DSL
        searchRequest.source().query(boolQueryBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            ans.setTotal(searchHits.getTotalHits().value);
            SearchHit[] hits = searchHits.getHits();
            List<QuestionTableVO> questionTableVOS = new ArrayList<>();
            for (SearchHit hit : hits) {
                questionTableVOS.add(JSONUtil.toBean(hit.getSourceAsString(), QuestionTableVO.class));
            }
            return questionTableVOS;
        } catch (IOException e) {
            throw new ZhimaOjException(ErrorCode.ELASTIC_SEARCH_ERROR);
        }
    }

    @Override
    public String saveOrUpdate(OjQuestionPublish ojQuestionPublish) {
        QuestionDoc questionDoc = BeanUtil.copyProperties(ojQuestionPublish, QuestionDoc.class, "scope");
        // 设置可见性
        questionDoc.setScope(ojQuestionPublish.getScope().getScope());
        // 重新设置题号为前缀和题号
        questionDoc.setNumber(ojQuestionPublish.getPrefix() + ojQuestionPublish.getNumber());
        // 只需要保存文字内容
        questionDoc.setLabels(ojQuestionPublish.getLabels().stream().map(SimpleLabelVO::getName).collect(Collectors.toSet()));
        // 题目内容就是背景+描述
        questionDoc.setContent(ojQuestionPublish.getBackground() + " " + ojQuestionPublish.getDescription());
        try {
            if (restHighLevelClient.exists(new GetRequest(QUESTION_INDEX, questionDoc.getId().toString()), RequestOptions.DEFAULT)) {
                // 全量更新
                restHighLevelClient.update(
                        new UpdateRequest()
                                .index(QUESTION_INDEX)
                                .id(questionDoc.getId().toString())
                                .doc(JSONUtil.toJsonStr(questionDoc), XContentType.JSON),
                        RequestOptions.DEFAULT);
            } else {
                // 新增
                restHighLevelClient.index(
                        new IndexRequest()
                                .index(QUESTION_INDEX)
                                .id(questionDoc.getId().toString())
                                .source(JSONUtil.toJsonStr(questionDoc), XContentType.JSON),
                        RequestOptions.DEFAULT);
            }
            // 表示成功
            return "";
        } catch (IOException e) {
            return String.format("更新ES索引失败，失败原因：%s", e.getMessage());
        }
    }

    @Override
    public boolean removeQuestionPublish(Long questionId) {
        try {
            restHighLevelClient.delete(new DeleteRequest().index(QUESTION_INDEX).id(questionId.toString()), RequestOptions.DEFAULT);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    private void buildBoolQueryBuilder(BoolQueryBuilder boolQueryBuilder, QueryQuestionCondition queryQuestionCondition) {
        BoolQueryBuilder shouldQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("scope", QuestionScope.PUBLIC.getScope()));
        boolQueryBuilder.must(shouldQueryBuilder);
        // must(参与算分)
        if (StrUtil.isNotBlank(queryQuestionCondition.getContent())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("content", queryQuestionCondition.getContent()));
        }
        if (StrUtil.isNotBlank(queryQuestionCondition.getTitle())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", queryQuestionCondition.getTitle()));
        }
        if (queryQuestionCondition.getLabels() != null && !queryQuestionCondition.getLabels().isEmpty()) {
            for (String label : queryQuestionCondition.getLabels()) {
                // 只要包含选择的其中标签之一即可
                // should
                shouldQueryBuilder.should(QueryBuilders.matchQuery("labels", label));
            }
        }
        if (StrUtil.isNotBlank(queryQuestionCondition.getNumber())) {
            // filter条件
            boolQueryBuilder.filter(QueryBuilders.termQuery("number", queryQuestionCondition.getNumber()));
        }
        if (queryQuestionCondition.getDifficulty() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("difficulty", queryQuestionCondition.getDifficulty()));
        }

    }
}
